﻿using System;
using System.Drawing;
using System.Windows.Forms;

namespace DrawTools.Editor.CanvasFigures
{
  public abstract class CanvasRectangleBase : CanvasObject
  {
    private Rectangle _rectangle;

    /// <summary>
    ///   Get number of handles
    /// </summary>
    public override int HandlesCount
    {
      get { return 8; }
    }

    public Rectangle Rectangle
    {
      get { return _rectangle; }
      set { _rectangle = value; }
    }

    public Color BackColor { get; set; }

    public byte BackgroundOpacity { get; set; }

    protected CanvasRectangleBase(int x, int y, int width, int height)
    {
      _rectangle.X = x;
      _rectangle.Y = y;
      _rectangle.Width = width;
      _rectangle.Height = height;

      BackColor = Color.White;
      BackgroundOpacity = 0;
    }

    /// <summary>
    ///   Hit test.
    ///   Return value: -1 - no hit
    ///   0 - hit anywhere
    ///   > 1 - handle number
    /// </summary>
    /// <param name="point"></param>
    /// <returns></returns>
    public override int HitTest(Point point)
    {
      if (IsSelected)
      {
        for (int i = 1; i <= HandlesCount; i++)
        {
          if (GetHandleRectangle(i).Contains(point))
            return i;
        }
      }

      if (PointInObject(point))
        return 0;

      return -1;
    }

    public override Rectangle GetBounds()
    {
      return Rectangle;
    }

    /// <summary>
    ///   Get cursor for the handle
    /// </summary>
    /// <param name="handleNumber"></param>
    /// <returns></returns>
    public override Cursor GetHandleCursor(int handleNumber)
    {
      switch (handleNumber)
      {
        case VertexType.LeftTop:
          return Cursors.SizeNWSE;
        case VertexType.Top:
          return Cursors.SizeNS;
        case VertexType.RightTop:
          return Cursors.SizeNESW;
        case VertexType.Right:
          return Cursors.SizeWE;
        case VertexType.RightBottom:
          return Cursors.SizeNWSE;
        case VertexType.Bottom:
          return Cursors.SizeNS;
        case VertexType.LeftBottom:
          return Cursors.SizeNESW;
        case VertexType.Left:
          return Cursors.SizeWE;
        default:
          return Cursors.Default;
      }
    }

    /// <summary>
    ///   Move handle to new point (resizing)
    /// </summary>
    /// <param name="point"></param>
    /// <param name="handleNumber"></param>
    /// <param name="shiftPressed"></param>
    public override void MoveHandleTo(Point point, int handleNumber, bool shiftPressed)
    {
      int left = Rectangle.Left;
      int top = Rectangle.Top;
      int right = Rectangle.Right;
      int bottom = Rectangle.Bottom;

      switch (handleNumber)
      {
        case VertexType.LeftTop:
          left = point.X;
          top = point.Y;
          break;
        case VertexType.Top:
          top = point.Y;
          break;
        case VertexType.RightTop:
          right = point.X;
          top = point.Y;
          break;
        case VertexType.Right:
          right = point.X;
          break;
        case VertexType.RightBottom:
          right = point.X;
          bottom = point.Y;
          break;
        case VertexType.Bottom:
          bottom = point.Y;
          break;
        case VertexType.LeftBottom:
          left = point.X;
          bottom = point.Y;
          break;
        case VertexType.Left:
          left = point.X;
          break;
      }

      _rectangle.X = left;
      _rectangle.Y = top;
      _rectangle.Width = right - left;
      _rectangle.Height = bottom - top;

      if (!shiftPressed)
        return;

      //TODO refactor this later. this looks ugly
      switch (handleNumber)
      {
        case VertexType.LeftTop:
        {
          _rectangle = MakeSquare(_rectangle);
          _rectangle.X = right - _rectangle.Width;
          _rectangle.Y = bottom - _rectangle.Height;
          break;
        }

        case VertexType.RightBottom:
        {
          _rectangle = MakeSquare(_rectangle);
          break;
        }

        case VertexType.LeftBottom:
        {
          _rectangle = MakeSquare(_rectangle);
          _rectangle.X = right - _rectangle.Width;
          _rectangle.Y = top;
          break;
        }
        case VertexType.RightTop:
        {
          _rectangle = MakeSquare(_rectangle);
          _rectangle.X = left;
          _rectangle.Y = bottom - _rectangle.Height;
          break;
        }

        case VertexType.Bottom:
        case VertexType.Top:
          _rectangle.Width = SignMultiplier(_rectangle.Width) * Math.Abs(_rectangle.Height);
          break;

        case VertexType.Right:
        case VertexType.Left:
          _rectangle.Height = SignMultiplier(_rectangle.Height) * Math.Abs(_rectangle.Width);
          break;
      }
    }

    public override bool IntersectsWith(Rectangle rectangle)
    {
      //return rectangle.Contains(Rectangle);
      return Rectangle.IntersectsWith(rectangle);
    }

    /// <summary>
    ///   Move object
    /// </summary>
    /// <param name="deltaX"></param>
    /// <param name="deltaY"></param>
    public override void Move(int deltaX, int deltaY)
    {
      _rectangle.X += deltaX;
      _rectangle.Y += deltaY;
    }

    /// <summary>
    ///   Normalize rectangle
    /// </summary>
    public override void Normalize()
    {
      _rectangle = GetNormalizedRectangle(_rectangle);
    }

    /// <summary>
    ///   Get handle point by 1-based number
    /// </summary>
    /// <param name="handleNumber"></param>
    /// <returns></returns>
    protected override Point GetHandle(int handleNumber)
    {
      int xCenter = _rectangle.X + _rectangle.Width / 2;
      int yCenter = _rectangle.Y + _rectangle.Height / 2;
      int x = _rectangle.X;
      int y = _rectangle.Y;

      switch (handleNumber)
      {
        case VertexType.LeftTop:
          x = _rectangle.X;
          y = _rectangle.Y;
          break;
        case VertexType.Top:
          x = xCenter;
          y = _rectangle.Y;
          break;
        case VertexType.RightTop:
          x = _rectangle.Right;
          y = _rectangle.Y;
          break;
        case VertexType.Right:
          x = _rectangle.Right;
          y = yCenter;
          break;
        case VertexType.RightBottom:
          x = _rectangle.Right;
          y = _rectangle.Bottom;
          break;
        case VertexType.Bottom:
          x = xCenter;
          y = _rectangle.Bottom;
          break;
        case VertexType.LeftBottom:
          x = _rectangle.X;
          y = _rectangle.Bottom;
          break;
        case VertexType.Left:
          x = _rectangle.X;
          y = yCenter;
          break;
      }

      return new Point(x, y);
    }

    protected override bool PointInObject(Point point)
    {
      return _rectangle.Contains(point);
    }

    protected Brush CreateBackBrush()
    {
      return new SolidBrush(Color.FromArgb(BackgroundOpacity, BackColor));
    }

    private static Rectangle MakeSquare(Rectangle rectangle)
    {
      int absValue = Math.Max(Math.Abs(rectangle.Width), Math.Abs(rectangle.Height));
      rectangle.Height = absValue * SignMultiplier(rectangle.Height);
      rectangle.Width = absValue * SignMultiplier(rectangle.Width);
      return rectangle;
    }

    private static int SignMultiplier(int value)
    {
      if (value < 0)
        return -1;
      return 1;
    }

    #region Helper Functions

    public static Rectangle GetNormalizedRectangle(int x1, int y1, int x2, int y2)
    {
      if (x2 < x1)
      {
        int tmp = x2;
        x2 = x1;
        x1 = tmp;
      }

      if (y2 < y1)
      {
        int tmp = y2;
        y2 = y1;
        y1 = tmp;
      }

      return new Rectangle(x1, y1, x2 - x1, y2 - y1);
    }

    public static Rectangle GetNormalizedRectangle(Point p1, Point p2)
    {
      return GetNormalizedRectangle(p1.X, p1.Y, p2.X, p2.Y);
    }

    public static Rectangle GetNormalizedRectangle(Rectangle r)
    {
      return GetNormalizedRectangle(r.X, r.Y, r.X + r.Width, r.Y + r.Height);
    }

    #endregion
  }
}